#pragma once

#include "type.h"

#include "Hull.h"

#include "Chunk.h"

namespace ChunkSystem
{
	class ChunkFileSerializer;
	class ExternalChunk;

	//used for camera focus
	template< class Unit, int SPAN = 63 >
	class TGrid
	{
	public:

		enum { SPANM = SPAN + 1 };
		enum { SPANH = SPANM / 2 };

		TGrid( void )
		{
			::memset( m_Units, 0, 4 * SPANM * SPANM );
		}
		~TGrid( void )
		{
			for( int iRow = 0 ; iRow != SPANM; ++iRow )
				for( int iCol = 0 ; iCol != SPANM; ++iCol )
					delete m_Units[iRow][iCol];
		}

		void SetOrigin( int16 x , int16 y );

		bool InRange( int16 x , int16 y ) const
		{
			if( x > m_centerGridPos.first + SPANH || x < m_centerGridPos.first - SPANH )
				return false;
			if( y > m_centerGridPos.second + SPANH || y < m_centerGridPos.second - SPANH )
				return false;

			return true;
		}
			
		Unit*& operator()( int16 x , int16 y);
		const Unit* operator()( int16 x , int16 y) const;
	
	private:

		void _DeleteEntry( int16 x , int16 y );

	private:

		GridCoord	m_centerGridPos;
		Unit*	m_Units[SPANM][SPANM];
	};


	class ChunkHull;

	class Column
	{
	public:
		
		typedef std::vector< ChunkHull* >		ChunkHulls;

		Column();
		~Column();

		ExternalChunk* GetExternalChunk() const{ return m_pExternalChunk; }
		Chunk* FindChunk(const Common::Vector3& vTarPos, Chunk* pIgnore = NULL ) const;
		void AddChunk( HullBorder& rBorders , Chunk* pChunk );

	private:
		ExternalChunk*	m_pExternalChunk;
		GridCoord		m_pos;

		HullTree		m_HullTree;
		ChunkHulls		m_holdingChunks;
	};

	class IChunkSpaceEventListener
	{
	public:
		virtual void OnAddIdenticalChunk( Chunk* pAddChunk ) = 0;
		virtual void OnRemoveChunk( Chunk* pRemoveChunk ) = 0;
	};
	typedef std::vector<IChunkSpaceEventListener*>	ChunkSpaceEventListeners;
	typedef ChunkSpaceEventListeners::iterator	ItrChunkSpaceEventListeners;

	class ChunkSpace
	{
	public:
		typedef TGrid< Column >				FocusGrid;
		
		typedef std::map<String , Chunk*>	 CurrentChunkMap;
		typedef std::map<GridCoord , Chunk*> GridChunkMap;
		
		typedef std::vector<Chunk*>			BlurredChunks;

		ChunkSpace(const String& strSpaceName );
		~ChunkSpace(void);

		void AddIdenticalChunk( Chunk*& pNewChunk );
		void RemoveChunk( Chunk* pChunk );

		void Focus( const Common::Vector3& focusTarget );

		Column* GetColumn( const Common::Vector3& vPos , bool bCreateIfNotFound = false );

		GridCoord PosToGridCoord( const Common::Vector3& rPos ) const;
		Common::Vector3 GridCoordToPos( const GridCoord& rGridCoord );

		Chunk* GetChunkByPointTest( const Common::Vector3& pos );
		Chunk* GetChunkByGridCoord( const GridCoord& rCoord );
		
		//always called when camera enter a space without a chunk created currently
		Chunk* CreateChunkFromChunkFile( const Common::Vector3& pos , ChunkFileSerializer* pFileSerializer );

		String GridCoordToExternalChunkPath( const GridCoord& rCoord ) const;

		float GetGridResolution() const {return m_fGridResolution;}

		CurrentChunkMap& GetCurrentChunk(void);

		void RegisterListener( IChunkSpaceEventListener* pListener );
		void UnregisterListener( IChunkSpaceEventListener* pListener );

		Chunk* GetHeaven(void);
		Chunk* GetEarth(void);

		void NoticeChunk( Chunk* pBindedChunk );
		void IgnoreChunk( Chunk* pIgnoreChunk );

	private:

		float				m_fGridResolution;
		float				m_fFocusRange;

		String				m_strSpaceName;

		GridChunkMap		m_GridChunkMap;
		CurrentChunkMap		m_CurrentChunkMap;

		FocusGrid			m_FocusGrid;
		BlurredChunks		m_blurredChunks;

		ChunkSpaceEventListeners	m_conListeners;

		Chunk*				m_pSky;
		Chunk*				m_pEarth;
	};
}